package rabbitmq

import (
	"github.com/streadway/amqp"
)

//@link https://www.cnblogs.com/linyue09/p/14156046.html
type RabbitMQ struct {
	//连接
	conn *amqp.Connection
	//管道
	channel *amqp.Channel
	//连接信息
	mqurl string
}

/*
创建RabbitMQ结构体实例

url amqp://账号:密码@地址:端口号/vhost  "amqp://user:pass@192.168.56.178:5677/v1"
*/
func NewRabbitMQ(url string) (*RabbitMQ, error) {
	rabbitmq := &RabbitMQ{mqurl: url}
	var err error
	//创建rabbitmq连接
	if rabbitmq.conn, err = amqp.Dial(rabbitmq.mqurl); err != nil {
		return nil, err
	}

	if rabbitmq.channel, err = rabbitmq.conn.Channel(); err != nil {
		return nil, err
	}
	return rabbitmq, nil
}

func (r *RabbitMQ) Version() string {
	return "v1.0.0"
}

//断开channel和connection
func (r *RabbitMQ) Destory() (err error) {
	if err = r.channel.Close(); err != nil {
		return
	}
	err = r.conn.Close()
	return
}

/*
消息发送

exchange string 交换机

key string Routing key

msg string 内容
*/
func (r *RabbitMQ) Publish(exchange string, key string, msg string) (err error) {
	err = r.channel.Publish(
		exchange,
		key,
		false,
		false,
		amqp.Publishing{
			//类型
			ContentType: "text/plain",
			//消息
			Body: []byte(msg),
		})
	return
}

/*
队列监听

queue string 队列名称

consumer string 消费者名称

autoAck bool 当autoAck(也称为noAck)为true时，服务器将确认在将交付内容写入网络之前，交付给该消费者。当autoAck是true，消费者不应该调用Delivery.Ack。自动确认交付意味着一些交付可能会丢失，如果消费者在服务器交付它们之后无法处理它们。

exclusive bool 当exclusive为true时，服务器将确保这是唯一的消费者从这个队列。当exclusive为false时，服务器将公平分布跨多个消费者交付。

noWait bool 当noWait为true时，不等待服务器确认请求立即开始交付。如果无法使用，则使用通道将引发异常并关闭通道。
*/
func (r *RabbitMQ) Consume(queue string, consumer string, autoAck bool, exclusive bool, noWait bool, callback func(msg string)) (err error) {
	//消费消息
	message, err := r.channel.Consume(
		queue,
		consumer,
		autoAck,
		exclusive,
		false,
		noWait,
		nil,
	)
	forever := make(chan bool)
	go func() {
		for d := range message {
			callback(string(d.Body))
		}
	}()
	// fmt.Println(queue + "监听中，退出请按 Ctrl+C")
	<-forever
	return
}

/*
创建交换机

exchange string 交换机名称

kind string {"direct":"直接","topic":"主题","fanout":"广播"}

durable bool 是否持久化

autoDelete bool 持久交换和自动删除交换将在服务器重启后继续存在当没有剩余绑定时，在服务器重启之前和之后移除。
这些交换对于健壮的临时拓扑或在需要时非常有用将持久队列绑定到自动删除的交换器。

internal bool true表示这个exchange不可以被client用来推送消息，仅用来进行exchange和exchange之间的绑定

noWait bool 是否阻塞 true表示要等待服务器的响应
*/
func (r *RabbitMQ) ExchangeDeclare(exchange string, kind string, durable bool, autoDelete bool, internal bool, noWait bool) (err error) {
	err = r.channel.ExchangeDeclare(
		exchange,
		kind,
		durable,
		autoDelete,
		internal,
		noWait,
		nil,
	)
	return
}

/*
创建队列

queue string 队列名称

durable bool 是否持久化

autoDelete bool 是否字段删除

exclusive bool 独占队列仅由声明它们的连接访问，并将在连接关闭时删除。其他连接上的通道在试图声明、绑定、使用、清除或删除同名队列时将收到错误。

noWait bool 是否阻塞 true表示要等待服务器的响应
*/
func (r *RabbitMQ) QueueDeclare(queue string, durable bool, autoDelete bool, exclusive bool, noWait bool) (err error) {
	_, err = r.channel.QueueDeclare(
		queue,
		durable,
		autoDelete,
		exclusive,
		noWait,
		nil,
	)
	return
}

/*
交换机队列绑定

qname string 队列名称

key string Routing key

exchange string 交换机名称

noWait bool 是否阻塞 true表示要等待服务器的响应
*/
func (r *RabbitMQ) QueueBind(qname string, key string, exchange string, noWait bool) (err error) {
	err = r.channel.QueueBind(
		qname,
		key,
		exchange,
		noWait,
		nil,
	)
	return
}
